import os

from salsa.task import Task
from salsa.util.io import IOMaster, IOMasterDead, RPCEncoderDecoder
from salsa.util.io import IOSlave, IOException, IOSlaveCommunicator
from salsa.util.rpc import RPCInfo, RPCDispatcher, RPCClient

from salsa.coresvc.logger import Logger
from salsa.util import LoggerSeverity

from salsa import do_trace_interrupt, do_trace, trace_exec_flow

import salsa

import threading

class ServiceException(Exception):
    pass

class Service(Task):
    def __init__(self):
        Task.__init__(self)
        
        self.port = int(self.port)
        self.host = os.getenv('SALSA_HOST', self.host)
        
        salsa.core = self.core
        salsa.logger = self.logger
        salsa.service = self
        
        self.is_done = False
    
    def start(self):
        self.io_master = IOMaster(self.port, self.host, 
                                  RPCEncoderDecoder())
        self.io_master.start()
        
        self.rpc_disp = RPCDispatcher(self.rpc_info)
        self.rpc_disp.bind(self)
        
        self.logger.log(LoggerSeverity.LS_NOTICE, 'Service %s is listening on %s:%s' % 
                            (self.name, self.host, self.port))
    
    def run(self):
        io_master = self.io_master
        
        while not self.is_done:
            try:
                io_request = io_master.get()                       
                request = io_request.get()
                
                reply = self.rpc_disp.process(request)
                    
                io_request.put(reply)
            except IOMasterDead:
                break
    
    def do_stop(self):
        pass
    
    def stop(self):
        if not self.is_done:
            Task.stop(self)
            
            self.is_done = True
            
            self.do_stop()
            
            self.io_master.stop()
        
        if trace_exec_flow: do_trace('Service %s is stopped' % self.name)

class Client(RPCClient):
    def __init__(self, svc_name, svc_class):
        try:
            self.svc_name = svc_name
            self._set_service()
            
            self.io_slave = IOSlave(host = self.svc_host, port = self.svc_port, 
                                         enc_dec = RPCEncoderDecoder())
            self.io_slave.start()
            
            RPCClient.__init__(self, svc_class.rpc_info, IOSlaveCommunicator(self.io_slave))
            
            if trace_exec_flow: do_trace('Client for %s is started' % self.svc_name)
        except IOException as e:
            raise ServiceException("Couldn't connect to service %s: %s" % (self.svc_name, str(e)))
    
    def __del__(self):
        self.stop()
    
    def _set_service(self):
        svc_list = salsa.core.list_services()
        
        for svc in svc_list:
            if svc['name'] == self.svc_name:
                self.svc_host = svc['host']
                self.svc_port = svc['port']
                
                break
        else:
            raise ServiceException('Service %s not found' % (self.svc_name))
    
    def stop(self):
        self.io_slave.stop()
        
        if trace_exec_flow: do_trace('Client for %s is stopped' % self.svc_name)